//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using ManagementStudioServices.Interfaces;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using SR = Microsoft.Research.DataLayer;

    /// <summary>
    /// Presentation logic for the DataReferenceStoreCenterPanePresenter.
    /// </summary>
    public class DataReferenceStoreCenterPanePresenter : IRegistryManagerCenterPanePresenter, INotifyPropertyChanged
    {
        /// <summary>
        /// Is removing the model from the parent.
        /// </summary>
        private bool isRemoving = false;

        /// <summary>
        /// The unity container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Service for registry access.
        /// </summary>
        private IRegistryManagerService registryManagerService;

        /// <summary>
        /// Editable data reference store model.
        /// </summary>
        private DataReferenceStoreModel currentModel;

        /// <summary>
        /// Selected data reference store model.
        /// </summary>
        private DataReferenceStoreModel selectedModel;

        /// <summary>
        /// The current model.
        /// </summary>
        public DataReferenceStoreModel CurrentModel
        {
            get
            {
                return this.currentModel;
            }
            set
            {
                this.currentModel = value;
                this.RaisePropertyChanged("CurrentModel");
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataReferenceStoreCenterPanePresenter"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="contentView">The content view.</param>
        /// <param name="service">The service.</param>
        public DataReferenceStoreCenterPanePresenter(
            IUnityContainer container,
            DataReferenceStoreCenterPane contentView,
            IRegistryManagerService service)
        {
            this.container = container;
            this.ContentView = contentView;
            this.registryManagerService = service;

            contentView.DataContext = this;

            this.SaveChangesCommand = new DelegateCommand<object>(p => this.SaveChangesExecute());

            this.ConfigureParametersCommand = new DelegateCommand<object>(p => this.ConfigureParmetersExecute());
            this.CancelChangesCommand = new DelegateCommand<object>(p => this.CancelChangesExecute());
            this.CreateNewStoreCommand = new DelegateCommand<object>(p => this.CreateNewStoreCommandExecute());
            this.DeleteStoreCommand = new DelegateCommand<object>(p => this.DeleteStoreCommandExecute());
        }

        /// <summary>
        /// Gets or sets the save changes command.
        /// </summary>
        /// <value>The save changes command.</value>
        public DelegateCommand<object> SaveChangesCommand { get; private set; }

        /// <summary>
        /// Gets or sets the configure parameters command.
        /// </summary>
        /// <value>The configure parameters command.</value>
        public DelegateCommand<object> ConfigureParametersCommand { get; private set; }

        /// <summary>
        /// Gets or sets the cancel changes command.
        /// </summary>
        /// <value>The cancel changes command.</value>
        public DelegateCommand<object> CancelChangesCommand { get; private set; }

        /// <summary>
        /// Gets or sets the delete store command.
        /// </summary>
        /// <value>The delete store command.</value>
        public DelegateCommand<object> DeleteStoreCommand { get; private set; }

        /// <summary>
        /// Gets or sets the create new store command.
        /// </summary>
        /// <value>The create new store command.</value>
        public DelegateCommand<object> CreateNewStoreCommand { get; private set; }

        #region IRegistryManagerCenterPanePresenter Members

        /// <summary>
        /// Gets or sets the content view.
        /// </summary>
        /// <value>The content view.</value>
        public System.Windows.UIElement ContentView { get; set; }

        /// <summary>
        /// Gets or sets the refresh command.
        /// </summary>
        /// <value>The refresh command.</value>
        public DelegateCommand<BaseRegMgrModel> RefreshCommand { get; set; }

        /// <summary>
        /// Gets or sets the delete command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> DeleteCommand { get; set; }

        /// <summary>
        /// Gets or sets the update command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> UpdateCommand { get; set; }

        /// <summary>
        /// Gets or sets the select root command.
        /// </summary>
        /// <value>The select root command.</value>
        public DelegateCommand<object> SelectRootCommand { get; set; }

        /// <summary>
        /// Gets or sets the selected model.
        /// </summary>
        /// <value>The selected model.</value>
        public BaseRegMgrModel SelectedModel
        {
            get
            {
                return this.selectedModel;
            }
            set
            {
                this.selectedModel = value as DataReferenceStoreModel;

                if (this.selectedModel != null)
                {
                    Guid currentID = this.selectedModel.Id;
                    if (this.CurrentModel != null && !this.CurrentModel.Id.Equals(currentID))
                    {
                        this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                    }

                    this.CurrentModel = DataReferenceStoreModel.CreateClone(this.selectedModel);

                    this.RaisePropertyChanged("SelectedModel");
                }
            }
        }

        /// <summary>
        /// Saves the changes.
        /// </summary>
        /// <returns>
        /// An enum value indicating the save status of the save operation, it can be Valid or Invalid.
        /// </returns>
        public SaveStateHandler SaveChanges()
        {
            return this.EnforceSaveChanges();
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the property changed.
        /// </summary>
        /// <param name="name">The name.</param>
        protected void RaisePropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Saves the changes execute.
        /// </summary>
        /// <param name="saveSuccess">The save success.</param>
        private SaveStateHandler SaveChangesExecute()
        {
            bool saveSucceeded = false;
            SR.DataReferenceStore store = null;

            StringBuilder errorMsg = new StringBuilder();
            try
            {
                if (!this.IsValidParameters())
                {
                    errorMsg.AppendLine(ManagementStudioResourceManager.GetString("DataStoreParametersNotConfigured"));
                }
                else if (!this.IsValidName())
                {
                    if (this.CurrentModel.Name == null ||
                        (this.CurrentModel.Name != null && string.IsNullOrEmpty(this.CurrentModel.Name.Trim())) ||
                        !Helper.CheckNameForValidCharacters(this.CurrentModel.Name.Trim()))
                    {
                        errorMsg.AppendLine(ManagementStudioResourceManager.GetString("DataStoreNameInvalid"));
                    }
                    else
                    {
                        errorMsg.AppendLine(ManagementStudioResourceManager.GetString("DataStoreNameAlreadyExists"));
                    }
                }
                else
                {
                    // Store the data reference store configured.
                    store = this.registryManagerService.SaveDataReferenceStore(this.CurrentModel);
                    if (store != null)
                    {
                        saveSucceeded = true;
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                errorMsg.Append(ex.Message);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleUIException(ex);
                errorMsg.Append(ex.Message);
            }

            SaveStateHandler saveSuccess = null;
            if (saveSucceeded)
            {
                this.selectedModel.Update(store, this.selectedModel.Parent as DataReferenceProviderModel);
                this.CurrentModel = DataReferenceStoreModel.CreateClone(this.selectedModel);

                this.container.Resolve<IStatusMessageControl>()
                    .SetText(ManagementStudioResourceManager.GetString("DataStoreSaveSuccess"));

                saveSuccess = new SaveStateHandler(SaveStatus.Valid);
            }
            else
            {
                TridentMessageBox.ShowTridentErrorMessageBox(errorMsg.ToString());
                saveSuccess = new SaveStateHandler(SaveStatus.Invalid);
            }

            if (!saveSucceeded && !this.SelectedModel.IsSelected)
            {
                isRemoving = true;

                // If the save has failed, reset the previous model as selected. The current selection will be what the 
                // user has selected on the left pane, which in turn launched this flow.
                DataReferenceStoreModel backUpModel = DataReferenceStoreModel.CreateClone(this.CurrentModel);
                this.Rollback();

                if (this.CurrentModel.IsNew)
                {
                    this.CreateNewStoreCommand.Execute(null);
                }
                else
                {
                    this.selectedModel.IsSelected = true;
                }

                isRemoving = false;

                this.CurrentModel = DataReferenceStoreModel.CreateClone(backUpModel);
            }

            return saveSuccess;
        }

        /// <summary>
        /// Configures the parmeters .
        /// </summary>
        private void ConfigureParmetersExecute()
        {
            try
            {
                this.InitializeProvider();

                if (!string.IsNullOrEmpty(this.CurrentModel.Parameters))
                {
                    SR.ExternalRegistryHelperProxy.SetParameters(this.selectedModel.Parent.Id, this.CurrentModel.Parameters);
                }

                string parameters = SR.ExternalRegistryHelperProxy.GetParameters(this.selectedModel.Parent.Id);

                if (!string.IsNullOrEmpty(parameters))
                {
                    this.CurrentModel.Parameters = parameters;
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }
        }

        /// <summary>
        /// Determines whether the data store parameters is configured or not.
        /// If parameters are configured then, parameters are updated in data store.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if data store parameters is configured; otherwise, <c>false</c>.
        /// </returns>
        private bool IsValidParameters()
        {
            return !string.IsNullOrEmpty(this.CurrentModel.Parameters);
        }

        /// <summary>
        /// Determines whether the data store name is valid or not.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if data store name; otherwise, <c>false</c>.
        /// </returns>
        private bool IsValidName()
        {
            bool isValidName = false;
            if (this.CurrentModel.Name != null &&
                !string.IsNullOrEmpty(this.CurrentModel.Name.Trim()) &&
                this.CurrentModel.Parent != null &&
                Helper.CheckNameForValidCharacters(this.CurrentModel.Name.Trim()))
            {
                isValidName = !this.CurrentModel.Parent.Children
                    .Any(child => child.Name.Equals(this.CurrentModel.Name.Trim(), StringComparison.OrdinalIgnoreCase)
                    && !child.Id.Equals(this.CurrentModel.Id));
            }

            return isValidName;
        }

        /// <summary>
        /// Cancels the changes execute.
        /// </summary>
        private void CancelChangesExecute()
        {
            this.isRemoving = true;

            this.Rollback();

            if (this.SelectRootCommand != null)
            {
                this.SelectRootCommand.Execute(null);
            }

            this.isRemoving = false;
        }

        /// <summary>
        /// Rollbacks this instance.
        /// </summary>
        private void Rollback()
        {
            if (this.CurrentModel.IsNew)
            {
                if (this.SelectedModel.Parent.Children.Contains(this.SelectedModel))
                {
                    this.SelectedModel.Parent.Children.Remove(this.SelectedModel);
                }
            }
            else if (this.SelectedModel != null)
            {
                this.CurrentModel = DataReferenceStoreModel.CreateClone(this.SelectedModel as DataReferenceStoreModel);
            }
        }

        /// <summary>
        /// Creates the new store command execute.
        /// </summary>
        private void CreateNewStoreCommandExecute()
        {
            SaveStateHandler saveState = this.EnforceSaveChanges();
            if (saveState.SaveOptionSelected == SaveStatus.Valid)
            {
                DataReferenceStoreModel model = new DataReferenceStoreModel(string.Empty, this.SelectedModel.Parent as DataReferenceProviderModel);
                this.SelectedModel.Parent.Children.Add(model);
                model.IsSelected = true;
            }
        }

        /// <summary>
        /// Deletes the data reference provider command execute.
        /// </summary>
        private void DeleteStoreCommandExecute()
        {
            if (this.CurrentModel.CanDelete)
            {
                MessageBoxResult res = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("DataReferenceStorerDeleteConfirmationMsg"));
                if (res == MessageBoxResult.Yes)
                {
                    try
                    {
                        bool deleteSuccess = this.registryManagerService.DeleteDataReferenceStore(this.CurrentModel);
                        if (deleteSuccess)
                        {
                            if (this.DeleteCommand != null)
                            {
                                this.isRemoving = true;

                                this.DeleteCommand.Execute(this.SelectedModel);

                                this.isRemoving = false;

                                this.container.Resolve<IStatusMessageControl>()
                                    .SetText(ManagementStudioResourceManager.GetString("DataStoreDeleteSucess"));
                            }
                        }
                        else
                        {
                            TridentMessageBox.ShowTridentErrorMessageBox(
                                ManagementStudioResourceManager.GetString("DataReferenceStoreDeleteError"));
                        }
                    }
                    catch (TridentCustomException ex)
                    {
                        TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
                    }
                    catch (Exception ex)
                    {
                        TridentErrorHandler.HandleKnownExceptionsInUI(ex);
                    }
                }
            }
        }

        /// <summary>
        /// Enforces the save changes.
        /// </summary>
        /// <returns></returns>
        private SaveStateHandler EnforceSaveChanges()
        {
            bool doRollBack = false;

            SaveStateHandler saveState = new SaveStateHandler(SaveStatus.Valid);
            if (!isRemoving)
            {
                if (this.IsChanged())
                {
                    MessageBoxResult userResult = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("DataReferenceStoreChangeQuestion"));
                    if (userResult == MessageBoxResult.Yes)
                    {
                        return this.SaveChangesExecute();
                    }
                    else if (userResult == MessageBoxResult.No)
                    {
                        doRollBack = true;
                    }
                }
                else if (this.CurrentModel.IsNew)
                {
                    doRollBack = true;
                }

                if (doRollBack)
                {
                    this.isRemoving = true;
                    this.Rollback();
                    this.isRemoving = false;
                }
            }

            return saveState;
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        private void InitializeProvider()
        {
            SR.ExternalRegistryHelperProxy.CreateInstance(this.selectedModel.Parent.Id);

            SR.ExternalRegistryHelperProxy.InitializeConfigurator(this.selectedModel.Parent.Id);
        }

        /// <summary>
        /// Determines whether this instance is changed.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance is changed; otherwise, <c>false</c>.
        /// </returns>
        private bool IsChanged()
        {
            return this.CurrentModel != null && !this.CurrentModel.Equals(this.SelectedModel as DataReferenceStoreModel);
        }

        #endregion Private Methods
    }
}
